using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Collections.ObjectModel;
using System.Web.Services.Description;
using System.Xml.Schema;

namespace ESBSimpleSamples.ServiceClient.Generator.WSDL
{
    /// <summary>
    /// Represents the metadata for a SOAP method
    /// </summary>
    public class WSDLMethod
    {
        private MethodInfo _methodInfo;

        public MethodInfo MethodInfo
        {
            get { return _methodInfo; }
            set { _methodInfo = value; }
        }

        public string Name
        {
            get
            {
                return this._methodInfo.Name;
            }
        }

        private WSDLParameterCollection _parameters;

        public WSDLParameterCollection Parameters
        {
            get { return _parameters; }
            set { _parameters = value; }
        }

        public string SOAPAction
        {
            get
            {
                string action = UTF8EscapeString(((SoapOperationBinding)this.OperationBinding.Extensions.Find(typeof(SoapOperationBinding))).SoapAction);
                return action;
            }
        }

        static readonly char[] hexTable = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        
        // encodes a Unicode string into utf-8 bytes then copies each byte into a new Unicode string,
        // escaping bytes > 0x7f, per the URI escaping rules
        static string UTF8EscapeString(string source)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(source);
            StringBuilder sb = new StringBuilder(bytes.Length); // start out with enough room to hold each byte as a char
            for (int i = 0; i < bytes.Length; i++)
            {
                byte b = bytes[i];
                if (b > 0x7f)
                {
                    sb.Append('%');
                    sb.Append(hexTable[(b >> 4) & 0x0f]);
                    sb.Append(hexTable[(b) & 0x0f]);
                }
                else
                    sb.Append((char)b);
            }
            return sb.ToString();
        }

        OperationBinding OperationBinding
        {
            get
            {
                OperationBinding match = null;
                foreach (Binding binding in this._serviceDescription.Bindings)
                {
                    object extension = binding.Extensions.Find(typeof(SoapBinding));
                    if (extension != null)
                    {
                        foreach (OperationBinding operationBinding in binding.Operations)
                        {
                            string messageName = operationBinding.Input.Name;
                            if (string.IsNullOrEmpty(messageName))
                            {
                                messageName = operationBinding.Name;
                            }
                            if (messageName == this.Name && extension as SoapBinding != null)
                            {
                                match = operationBinding;
                            }
                        }
                    }
                }

                return match;
            }
        }

        Operation Operation
        {
            get
            {
                PortType portType = ServiceDescriptions.GetPortType(OperationBinding.Binding.Type);
                foreach (Operation operation in portType.Operations)
                {
                    if (operation.IsBoundBy(OperationBinding))
                    {
                        return operation;
                    }
                }
                return null;
            }
        }

        private ServiceDescriptionCollection ServiceDescriptions
        {
            get
            {
                ServiceDescriptionCollection coll = new ServiceDescriptionCollection();
                coll.Add(this._serviceDescription);
                return coll;
            }

        }
        public string RequestTypeName
        {
            get
            {

                Message inputMessage = ServiceDescriptions.GetMessage(this.Operation.Messages.Input.Message);
                return inputMessage.Parts[0].Element.Name;
            }
        }

        public string ResponseTypeName
        {
            get
            {
                Message inputMessage = ServiceDescriptions.GetMessage(this.Operation.Messages.Output.Message);
                return inputMessage.Parts[0].Element.Name;
            }
        }

        public string ResponseResultName
        {
            get
            {
                string responseResultName = null;
                //need to get this from the schema:
                foreach (XmlSchemaObject obj in this.Schema.Elements.Values)
                {
                    XmlSchemaElement element = obj as XmlSchemaElement;
                    if (element.Name == this.ResponseTypeName)
                    {
                        XmlSchemaComplexType typ = (XmlSchemaComplexType)element.ElementSchemaType;
                        responseResultName = ((XmlSchemaElement)((System.Xml.Schema.XmlSchemaSequence)typ.Particle).Items[0]).Name;
                    }
                }
                return responseResultName;
            }
        }

        private XmlSchema _schema;

        public XmlSchema Schema
        {
            get { return _schema; }
            set { _schema = value; }
        }

        private ServiceDescription _serviceDescription;

        public WSDLMethod() { }

        public WSDLMethod(MethodInfo methodInfo, ServiceDescription serviceDescription, XmlSchema schema)
        {
            this._methodInfo = methodInfo;
            this._serviceDescription = serviceDescription;
            this._schema = schema;
            this._parameters = new WSDLParameterCollection();
            foreach (ParameterInfo parameter in methodInfo.GetParameters())
            {
                this._parameters.Add(new WSDLParameter(parameter));
            }
        }

        public override string ToString()
        {
            return this.Name;
        }

    }

    public class WSDLMethodCollection : Collection<WSDLMethod>
    {
    }
}
